# Copyright (C) 2011 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING3.  If not see
# <http://www.gnu.org/licenses/>.

proc load_gcc_lib { filename } {
    global srcdir loaded_libs
    load_file $srcdir/../../gcc/testsuite/lib/$filename
    set loaded_libs($filename) ""
}

load_lib dg.exp
load_gcc_lib target-libpath.exp
load_gcc_lib wrapper.exp
# Required by target-supports-dg.exp.
load_gcc_lib target-supports.exp
# For dg-skip-if.
load_gcc_lib target-supports-dg.exp
load_gcc_lib target-utils.exp
# For ${tool}_exit.
load_gcc_lib gcc-defs.exp
# For prune_gcc_output.
load_gcc_lib prune.exp

set dg-do-what-default run

# Define boehm-gc callbacks for dg.exp.

# FIXME: The next two are independent of boehm-gc; move to some library.
proc ${tool}-dg-test-1 { target_compile prog do_what extra_tool_flags } {

    # Set up the compiler flags, based on what we're going to do.

    set options [list]
    switch $do_what {
	"compile" {
	    set compile_type "assembly"
	    set output_file "[file rootname [file tail $prog]].s"
	}
        "assemble" {
            set compile_type "object"
            set output_file "[file rootname [file tail $prog]].o"
        }
        "ltassemble" {
	    # Needs to be different from assemble since we cannot determine
	    # from $prog whether to produce a regular or a libtool object.
            set compile_type "object"
            set output_file "[file rootname [file tail $prog]].lo"
        }
	"link" {
	    set compile_type "executable"
	    # Don't add .exe extension, libtool may strip if from output file.
	    set output_file "[file rootname [file tail $prog]]"
	    # If we're linking a libtool object, produce a libtool library.
	    if { [file extension $prog] == ".lo" } {
		set output_file "lib$output_file.la"
	    }
	}
	"run" {
	    set compile_type "executable"
	    # FIXME: "./" is to cope with "." not being in $PATH.
	    # Should this be handled elsewhere?
	    # YES.
	    set output_file "./[file rootname [file tail $prog]]"
	    # This is the only place where we care if an executable was
	    # created or not.  If it was, dg.exp will try to run it.
	    remote_file build delete $output_file
	}
	default {
	    perror "$do_what: not a valid dg-do keyword"
	    return ""
	}
    }

    if { $extra_tool_flags != "" } {
	lappend options "additional_flags=$extra_tool_flags"
    }

    set comp_output [$target_compile "$prog" "$output_file" "$compile_type" $options];

    return [list $comp_output $output_file]
}

proc ${tool}-dg-test { prog do_what extra_tool_flags } {
    global tool

    return [${tool}-dg-test-1 ${tool}_target_compile $prog $do_what $extra_tool_flags]
}

proc boehm-gc-init { args } {
    global gluefile wrap_flags
    global srcdir
    global blddirgc
    global TOOL_EXECUTABLE
    global GCC_UNDER_TEST
    global objdir
    global gc_include
    global gc_lib
    global gc_lib_conv
    global tool
    global tool_root_dir
    global ld_library_path

    set blddirgc [lookfor_file [get_multilibs] ${tool}]
    verbose "blddirgc: $blddirgc"

    if ![info exists GCC_UNDER_TEST] then {
	if [info exists TOOL_EXECUTABLE] {
	    set GCC_UNDER_TEST $TOOL_EXECUTABLE
	} else {
	    set GCC_UNDER_TEST "[find_gcc]"
	}
    }

    set gccdir [lookfor_file $tool_root_dir gcc/libgcc.a]
    if {$gccdir != ""} {
	set gccdir [file dirname $gccdir]
    }
    verbose "gccdir: $gccdir"

    set ld_library_path "."
    append ld_library_path ":${gccdir}"

    set compiler "${gccdir}/xgcc"
    if { [is_remote host] == 0 && [which $compiler] != 0 } {
	foreach i "[exec $compiler --print-multi-lib]" {
	    set mldir ""
	    regexp -- "\[a-z0-9=_/\.-\]*;" $i mldir
	    set mldir [string trimright $mldir "\;@"]
	    if { "$mldir" == "." } {
		continue
	    }
	    if { [llength [glob -nocomplain ${gccdir}/${mldir}/libgcc_s*.so.*]] >= 1 } {
		append ld_library_path ":${gccdir}/${mldir}"
	    }
	}
    }
    # Add the library path for boehm-gc.
    append ld_library_path ":${blddirgc}/.libs"

    # Add the library path for boehm-gc.lib libtool libraries.
    append ld_library_path ":.libs"

    verbose "ld_library_path: $ld_library_path"

    # Point to the headers in boehm-gc.
    set gc_include "${blddirgc}/include"
    verbose "gc_include: $gc_include"

    set gc_lib "${blddirgc}/libgcjgc.la"
    verbose "gc_lib: $gc_lib"

    set gc_lib_conv "${blddirgc}/libgcjgc_convenience.la"
    verbose "gc_lib_conv: $gc_lib_conv"

    set_ld_library_path_env_vars
    boehm-gc_maybe_build_wrapper "${objdir}/testglue.o"
}

proc boehm-gc_target_compile { source dest type options } {
    global gluefile wrap_flags;
    global srcdir
    global TOOL_OPTIONS
    global GCC_UNDER_TEST
    global libtool
    global gc_include
    global gc_lib
    global gc_lib_conv
    global threadcflags
    global threadlibs
    global extra_test_libs

    if { [target_info needs_status_wrapper]!="" && [info exists gluefile] } {
	lappend options "libs=${gluefile}"
	lappend options "ldflags=$wrap_flags"
    }

    # TOOL_OPTIONS must come first, so that it doesn't override testcase
    # specific options.
    if [info exists TOOL_OPTIONS] {
	lappend options [concat "additional_flags=$TOOL_OPTIONS" $options];
    }

    # Map type to libtool mode.
    switch $type {
	"object" {
	    set mode "compile"
	}
	"executable" {
	    set mode "link"
	}
	default {
	    perror "$type: unhandled type"
	    return ""
	}
    }

    # Set this once for reuse in boehm-gc.lib/lib.exp.
    set libtool "../libtool"
    # We have to run silently to avoid DejaGnu lossage.
    lappend options "compiler=$libtool --silent --tag=CC --mode=$mode \
    	$GCC_UNDER_TEST"

    lappend options "additional_flags=-I${gc_include} -I${srcdir}/../include"
    lappend options "additional_flags=${threadcflags}"

    lappend options "libs=-Wc,-shared-libgcc"
    
    if { [file extension $dest] == ".la" } {
	lappend options "libs=${gc_lib_conv}"
    } else {
	lappend options "libs=${gc_lib}"
    }
    lappend options "libs=${threadlibs} ${extra_test_libs}"

    verbose "options: $options"
    return [target_compile $source $dest $type $options]
}

# Prune harmless messages.
proc boehm-gc-dg-prune { system text } {
    # Prune the same messages as the gcc testsuite does.
    set text [prune_gcc_output $text]

    # Ignore harmless Darwin ranlib warnings.
    regsub -all "(^|\n)(\[^\n\]*ranlib: file: \[^\n\]* has no symbols\n?)+" $text "\\1" text

    return $text
}

# Build shared library from LIB.
proc boehm-gc-build-shlib { lib flags extra-flags } {
    global subdir
    global dg-do-what-default
    global libtool
    global gc_lib_conv

    # Determine basename.
    set basename "[file rootname [file tail $lib]]"
    verbose "basename: $basename"

    # DejaGnu 1.4.4 doesn't support nested calls to dg-test.  Restore
    # original state before calling it again.
    if { [info procs dg-save-unknown] != [list] } {
	rename unknown ""
	rename dg-save-unknown unknown
    }

    set saved-dg-do-what ${dg-do-what-default}

    # Compile $lib into libtool object.
    set dg-do-what-default ltassemble
    dg-test -keep-output $lib $flags ${extra-flags}

    # FIXME: Explain options.
    set shopt "-version-info 1:2:0 -no-undefined -rpath /nowhere"

    # There's no way to get output_file out of dg-test, only
    # ${tool}-dg-test returns it, so hardcode that here.
    set dg-do-what-default link
    dg-test -keep-output $basename.lo $flags "${extra-flags} $shopt"

    set dg-do-what-default ${saved-dg-do-what}

    # Restore unknown, dg-save-unknown as DejaGnu 1.4.4 dg-test expects them.
    if { [info procs dg-save-unknown] == [list] } {
    	rename unknown dg-save-unknown
	proc unknown {} {
	}
    }

    remote_exec host $libtool "--mode=clean rm -f $basename.lo"

    return lib$basename.la
}

# Handle dg-add-shlib.
proc dg-add-shlib { args } {
    global tool
    global srcdir subdir
    global shlib

    if { [llength $args] > 2 } {
	error "[lindex $args 0]: too many arguments"
	return
    }

    set lib [lindex $args 1]
    set lib "$srcdir/$subdir/$lib"
    verbose -log "dg-add-shlib $lib"

    upvar tool_flags flags
    upvar default_extra_tool_flags extra-flags
    # Build support library.
    set shlib [boehm-gc-build-shlib $lib $flags ${extra-flags}]

    uplevel lappend tool_flags $shlib
}

# Run boehm-gc testcases.
proc boehm-gc-dg-runtest { testcases flags extra-flags } {
    global runtests
    global libtool
    global shlib

    set shlib ""

    foreach testcase $testcases {
	# If we're only testing specific files and this isn't one of them,
	# skip it.
	if ![runtest_file_p $runtests $testcase] {
	    return
	}

	# Run testcase.
	# Keep output, otherwise libtool below won't remove the corresponding
	# file in .libs.
	dg-test -keep-output $testcase $flags ${extra-flags}

	# Remove $execname and eventually $shlib.
	remote_exec host $libtool "--mode=clean rm -f \
		[file rootname [file tail $testcase]] $shlib"
    }
}
